package org.hawk.gwt.ppc.introspection.impl;

/**
 * Provides tokenizer implementation for java language.
 * Tokenization is based on wherever token is java indentifier or not.
 * 
 * @see Character#isJavaIdentifierStart(char)
 * @see Character#isJavaIdentifierPart(char)
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class JavaTokenizer {

	private String src;
	private String current;
	private int index;
	private final int length;
	private char ch;
	
	/**
	 * Constructs tokenizer for string presentation of some part of java source.
	 * @param src
	 */
	public JavaTokenizer(final String source) {
		this.src = source.trim();
		this.index = 1;
		this.length = src.length();
		this.ch = length > 0 ? src.charAt(0) : 0;
	}
	
	/**
	 * @return current token returned from source
	 * @throws IndexOutOfBoundsException in case next() is never called on this instance
	 * or source of characters is empty.
	 */
	public String current() {
		if (current == null) {
			throw new IndexOutOfBoundsException("next() is never called or source is empty");
		}
		return current;
	}
	
	/**
	 * @return next token returned from source
	 * @throws IndexOutOfBoundsException in case hasNext() returns <code>false</code>.
	 */
	public String next() {
		if (!hasNext()) {
			throw new IndexOutOfBoundsException("Unable to continue - source of characters is empty");
		}
		StringBuilder result = new StringBuilder();
		
		if (Character.isJavaIdentifierStart(ch)) {
			result.append(doNext());
			while(index <= length && Character.isJavaIdentifierPart(ch)) {
				result.append(doNext());
			}
		} else {
			result.append(doNext());
		}
		while (Character.isWhitespace(ch) && index <= length) {
			doNext();
		}
		
		
		return current = result.toString();
	}
	
	private char doNext() {
		char c = ch;
		if (index < length) {
			ch = src.charAt(index++);
		} else {
			ch = 0;
			index++;
		}
		return c;
	}
	
	/**
	 * @return <code>true</code> if there are some tokens awailable in the source and false otherwise.
	 */
	public boolean hasNext() {
		return index <= length;
	}
}
